#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "icar.h"
#include "icar_log.h"
#include "icar_pkg.h"

static uint16_t icar_msg_seq = 0;

void
icar_pkg_print(uint8_t *src, size_t size)
{
	int i = 0;
	for (; i < size; i++)
		printf("%02x", src[i] & 0xff);

	printf("\n");
}

/*
   对有效数据进行校验，生成校验码，作为返回值
   src 不能为空，size要大于0
 */
uint8_t
icar_data_auth(uint8_t *src, size_t size)
{
	uint8_t code;
	int i;

	code = 0;
	for (i = 0; i < size; i++) {
		code ^= src[i];
	}

	return code;
}

/* 
 对数据包添加消息头0x7e和消息尾0x7e并对其进行转义，并填入dest中，
  源数据为src，size参数为src的存储大小
 */
int
icar_pkg_trans_add_head_tail(uint8_t *dest, uint8_t *src, size_t size)
{
	if (!dest || !src || size == 0)
	{
		icar_daemon(LOG_ERR, "Invalid parameters");
		return -1;
	}

	int count = 0;
	int i = 0;

	dest[count++] = 0x7e;

	for ( ;i < size; i++) {
		if (src[i] == 0x7e) {
			dest[count++] = 0x7d;
			dest[count++] = 0x02;
		} else if (src[i] == 0x7d) {
			dest[count++] = 0x7d;
			dest[count++] = 0x01;
		} else
			dest[count++] = src[i];
	}

	dest[count++] = 0x7e;

	return count;
}

/* 
   对从终端接收到的数据包，去掉包头包尾的0x7e，反转义，校验。
   若成功，返回有效数据包的长度，
   若失败，返回负值
 */
int
icar_pkg_del_head_tail(uint8_t *dest, uint8_t *src, size_t size)
{
	if (!dest || !src || size <= 0)
	{
		icar_daemon(LOG_ERR, "Invalid data format");
		return -1;
	}

	int count = 0;
	int i = 0;
	uint8_t code = 0;

	if ((src[0] != 0x7e) || src[size-1] != 0x7e)
	{
		icar_daemon(LOG_ERR, "Received eror message package");
		return -1;
	}

	for (i = 1; i < size-1; i++)
	{
		if (src[i] == 0x7d) {
			i++;
			if (src[i] == 0x02) {
				dest[count++] = 0x7e;
			} else if (src[i] == 0x01) {
				dest[count++] = 0x7d;
			} else
			{
				icar_daemon(LOG_ERR, "Received message has wrong format");
				return -1;
			}
		} else
			dest[count++] = src[i];
	}

	code = dest[count-1];

	if (code == icar_data_auth(dest, count-1)) {
		dest[count-1] = 0;
		count--;
	} else {
		icar_daemon(LOG_ERR, "Authenticate rises with an error");
		return -1;
	}

	return count;
}

int
icar_bcd(uint8_t *bcd, const char *number, size_t size)
{
	if (!bcd || !number)
	{
		icar_daemon(LOG_ERR, "BCD or Phone number is error");
		return -1;
	}

	if (size != 11 && size != 12)
	{
		icar_daemon(LOG_ERR, "Length of phone number is wrong");
		return -1;
	}

	int count = 0, i;

	for (i = 0; i < size; i++)
		if (!(number[i] >= '0' && number[i] <= '9'))
		{
			icar_daemon(LOG_ERR, "Invalid phone number");
			return -1;
		}

	if (size == 11)
	{
		i = 0;
		bcd[count++] = (uint8_t)(chtoi(number[i++]) & 0xff);
		for (count = 1; count < 6; count++)
		{
			bcd[count] = (chtoi(number[i]) << 4 | chtoi(number[i+1])) & 0xff;
			i += 2;
		}
	}
	else
	{
		for (count = 0; count < 6; i++)
		{
			bcd[count] = (chtoi(number[i]) << 4 | chtoi(number[i+1])) & 0xff;
			i += 2;
		}
	}

	return count;
}

struct icar_iovec *icar_pkg_iovec(uint16_t id, const char *number, uint8_t *message, size_t size)
{
	uint16_t property = 0;
	uint16_t stmp;
	uint8_t data[MAX_PKG_LENGTH] = { 0 };
	int n = 0;
	uint16_t total, left, i;
	uint8_t authcode = 0;
	struct icar_iovec *iovec;

	total = left = i = 0;
	iovec = NULL;

	if (message == NULL || size == 0)
		total = left = i = 0;
	else  {
		total = size / 1023;
		left = size % 1023;
	}

	if (total == 0) {
		iovec = icar_malloc_iovec(1, MAX_PKG_LENGTH * 2 + 2);
		if (iovec == NULL)
			return NULL;

		ICAR_HTONS(id);
		property = left;
		ICAR_HTONS(property);
		if (icar_bcd(&data[n], number, strlen(number)) != 6)
		{
			free_iovec(iovec);
			return NULL;
		}
		n += 6;
		ICAR_HTONS(icar_msg_seq++);
		memcpy(&data[n], message, size);
		n += left;
		authcode = icar_data_auth(data, n+1);
		data[n++] = authcode;
		n = icar_pkg_trans_add_head_tail(iovec->v[0].iov_base, data, n);
		if (n < 0) {
			free_iovec(iovec);
			return NULL;
		}
		iovec->v[0].iov_len = n;
		return iovec;
	} else {
		uint16_t pkg_total = 0;
		uint16_t seqstart;
		uint16_t seqend;
		if (left > 0)
			pkg_total = total + 1;
		else
			pkg_total = total;

		iovec = icar_malloc_iovec(pkg_total, MAX_PKG_LENGTH * 2 + 2);
		if (iovec == NULL)
			return NULL;

		seqstart = icar_msg_seq;
		seqend = icar_msg_seq + pkg_total -1;
		icar_msg_seq += pkg_total;

		for (i = 1; i <= total; i++) {
			ICAR_HTONS(id);
			property |= 0x3ff;
			property |= SUBPACKAGE;
			ICAR_HTONS(property);
			if (icar_bcd(&data[n], number, strlen(number)) != 6) {
				free_iovec(iovec);
				return NULL;
			}
			n += 6;
			ICAR_HTONS(seqstart+i-1);
			ICAR_HTONS(pkg_total);
			ICAR_HTONS(i);
			memcpy(&data[n], message + 1023 * (i-1), 1023);
			n += 1023;
			authcode = icar_data_auth(data, n+1);
			data[n++] = authcode;
			n = icar_pkg_trans_add_head_tail(iovec->v[i-1].iov_base, data, n);
			if (n < 0)
				free_iovec(iovec);
			iovec->v[i-1].iov_len = n;
			memset(data, 0, MAX_PKG_LENGTH);
			n = 0;
		}

		if (left == 0) {
			return iovec;
		}

		ICAR_HTONS(id);
		property = left;
		property |= SUBPACKAGE;
		ICAR_HTONS(property);
		if (icar_bcd(&data[n], number, strlen(number)) != 6) {
			free_iovec(iovec);
			return NULL;
		}
		n += 6;
		ICAR_HTONS(seqend);
		ICAR_HTONS(pkg_total);
		ICAR_HTONS(pkg_total);
		memcpy(&data[n], message+1023 * total, left);
		n += left;
		authcode = icar_data_auth(data, n+1);
		data[n++] = authcode;
		n = icar_pkg_trans_add_head_tail(iovec->v[pkg_total-1].iov_base, data, n);
		if (n < 0) {
			free_iovec(iovec);
			return NULL;
		}

		iovec->v[pkg_total-1].iov_len = n;
		return iovec;
	}

	return NULL;
}

struct icar_iovec *
icar_malloc_iovec(int nmemb, size_t size)
{
	struct icar_iovec *iovec = NULL;
	int i;
	if (nmemb <= 0)
		return NULL;
	iovec = (struct icar_iovec *)malloc(sizeof(struct icar_iovec) + nmemb * sizeof(struct iovector));
	if (iovec == NULL)
		return NULL;

	memset(iovec, 0, sizeof(struct icar_iovec) + nmemb * sizeof(struct iovector));

	iovec->total = nmemb;

	for (i = 0; i < nmemb; i++) {
		iovec->v[i].iov_base = (uint8_t *)malloc(size);
		if (iovec->v[i].iov_base == NULL) {
			free_iovec(iovec);
			return NULL;
		}
		memset(iovec->v[i].iov_base, 0, size);
		iovec->v[i].iov_len = 0;
	}

	return iovec;
}

void
icar_print_iovec(struct icar_iovec *v)
{
	if (!v || !(v->v) || v->total <= 0)
		return;

	int i = 0;
	for (i = 0; i < v->total; i++) 
		if (v->v[i].iov_base && v->v[i].iov_len > 0)
			icar_pkg_print(v->v[i].iov_base, v->v[i].iov_len);
}

void 
free_iovec(struct icar_iovec *v)
{
	int i;
	if (v == NULL)
		return;

	if (v->total <= 0)
		return;

	for (i = 0; i < v->total; i++) {
		if (v->v[i].iov_base)
		{
			free(v->v[i].iov_base);
			v->v[i].iov_base = NULL;
		}
	}

	free(v);
	return;
}
